﻿#region Copyright
/// <copyright>
/// Copyright (c) 2011 Ramunas Geciauskas, http://geciauskas.com
///
/// Permission is hereby granted, free of charge, to any person obtaining a copy
/// of this software and associated documentation files (the "Software"), to deal
/// in the Software without restriction, including without limitation the rights
/// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
/// copies of the Software, and to permit persons to whom the Software is
/// furnished to do so, subject to the following conditions:
///
/// The above copyright notice and this permission notice shall be included in
/// all copies or substantial portions of the Software.
///
/// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
/// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
/// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
/// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
/// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
/// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
/// THE SOFTWARE.
/// </copyright>
/// <author>Ramunas Geciauskas</author>
/// <summary>DeleteAfterRunning Main class</summary>
#endregion
			


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.IO;
using System.Reflection;
using System.Diagnostics;
using System.Reflection.Emit;

namespace DeleteAfterRunning
{
    public partial class MainForm : Form
    {
        private int progressStep = 0;
        

        private int iconsCount = 0;

        public MainForm()
        {
            InitializeComponent();

            backButton.Enabled = false;
            iconsCount = iconsList.Images.Count;
        }

        private void closeButton_Click(object sender, EventArgs e)
        {
            if (progressStep != 0)
                if (MessageBox.Show("Are you sure want to abort and close the program?", "Close", MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.No)
                    return;

            this.Close();
        }

        private void nextButton_Click(object sender, EventArgs e)
        {
            if (!ValidateNext())
                return;

            tabControl.SelectedIndex = (++progressStep);

            if (progressStep == 3)
            {
                backButton.Enabled = false;
                startOverButton.Enabled = false;
                openButton.Enabled = true;
                openButton.Text = "Cancel";
                this.Update();

                DoAction();
            }
        }

        private void tabControl_Selecting(object sender, TabControlCancelEventArgs e)
        {   
            tabControl.SelectedIndex = progressStep;
        }

        private void backButton_Click(object sender, EventArgs e)
        {
            tabControl.SelectedIndex = --progressStep;
        }

        private void tabControl_SelectedIndexChanged(object sender, EventArgs e)
        {
            backButton.Enabled = (tabControl.SelectedIndex > 0);
            nextButton.Enabled = (tabControl.SelectedIndex < 3);
        }

        private void sourceFileBrowse_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                return;

            sourceFileBox.Text = openFileDialog.FileName;
            saveFileDialog.FileName = System.IO.Path.GetFileNameWithoutExtension(sourceFileBox.Text);

            if (Path.GetExtension(sourceFileBox.Text) == ".exe")
                outputFileBox.Text = Path.GetDirectoryName(sourceFileBox.Text) + "\\" + Path.GetFileNameWithoutExtension(sourceFileBox.Text) + " - DAR.exe";
            else
                outputFileBox.Text = Path.GetDirectoryName(sourceFileBox.Text) + "\\" + Path.GetFileNameWithoutExtension(sourceFileBox.Text) + ".exe";
        }

        private void outputFIleBrowse_Click(object sender, EventArgs e)
        {
            if (saveFileDialog.ShowDialog() != System.Windows.Forms.DialogResult.OK)
                return;

            outputFileBox.Text = saveFileDialog.FileName;
        }

        private bool ValidateNext()
        {
            if (progressStep == 0)
                return true;

            if (!File.Exists(sourceFileBox.Text))
            {
                MessageBox.Show("Source file does not exist.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            try
            {
                if (Path.GetDirectoryName(outputFileBox.Text) == String.Empty ||
                    Path.GetDirectoryName(outputFileBox.Text) == null ||
                    Directory.Exists(Path.GetDirectoryName(outputFileBox.Text)) == false)
                {
                    MessageBox.Show("Cannot create output file with in specified location.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                    return false;
                }
            }
            catch (Exception)
            {
                MessageBox.Show("Cannot create output file with in specified location.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            if (progressStep == 1)
                GetIcons();

            return true;
        }


        private void GetIcons()
        {
            // remove the last icon (if user selected a new file)
            if (iconsView.Items.Count > iconsCount)
            {
                iconsView.Items.RemoveAt(0);
                iconsList.Images.RemoveAt(iconsList.Images.Count - 1);
            }
            
            Icon icon = null, targetIcon = null;
            Icon[] splitIcons = null;
            bool single = true;

            try
            {
                if (Path.GetExtension(sourceFileBox.Text) == ".ico")
                    icon = new Icon(sourceFileBox.Text);
                else if (Path.GetExtension(sourceFileBox.Text) == ".exe")
                {
                    IconExtractor ie = new IconExtractor(sourceFileBox.Text);
                    icon = ie.GetIcon(0);
                    single = false;
                }
                else
                {
                    IntPtr iconPtr;
                    IconExtractor.SHFILEINFO shinfo = new IconExtractor.SHFILEINFO();

                    iconPtr = IconExtractor.SHGetFileInfo(sourceFileBox.Text, 0,
                    ref shinfo, (uint)Marshal.SizeOf(shinfo),
                    IconExtractor.SHGFI_ICON | IconExtractor.SHGFI_LARGEICON);

                    icon = System.Drawing.Icon.FromHandle(shinfo.hIcon);
                }
            }
            catch (Exception)
            { }

            if (single)
            {
                targetIcon = icon;
            }
            else
            {
                splitIcons = IconExtractor.SplitIcon(icon);


                int maxSize = 0, maxIndex = 0, maxBit = 0;
                for (int i = 0; i < splitIcons.Length; i++)
                {
                    if (IconExtractor.GetIconBitDepth(splitIcons[i]) > maxBit)
                    {
                        maxBit = IconExtractor.GetIconBitDepth(splitIcons[i]);
                        maxIndex = i;
                        maxSize = splitIcons[i].Width;
                    }
                    else if (IconExtractor.GetIconBitDepth(splitIcons[i]) == maxBit)
                    {
                        if (splitIcons[i].Width > maxSize)
                        {   
                            maxSize = splitIcons[i].Width;
                            maxIndex = i;
                        }
                    }
                }

                targetIcon = splitIcons[maxIndex];
            }

                iconsList.Images.Add(targetIcon);

                iconsView.Items.Insert(0, "Current", iconsList.Images.Count - 1);

                iconsView.Items[0].Selected = true;
                iconsView.Select();

        }

        private void iconsView_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (iconsView.SelectedItems.Count <= 0)
                return;
            
            currentIcon.Image = iconsList.Images[iconsView.SelectedItems[0].ImageIndex];
        }

        private void DoAction()
        {
            progressBar.Minimum = 0;
            progressBar.Maximum = 100;
            progressBar.Value = 0;
            statusLabel.Text = "Initializing...";

            this.Update();
            backgroundWorker.RunWorkerAsync();
        }

        private void ExtractResource(string name, string path)
        {
            Assembly currentAssembly = Assembly.GetExecutingAssembly();
            string[] resources = currentAssembly.GetManifestResourceNames();

            string resName = "";
            foreach (string s in resources)
                if (s.ToLower().Contains(name.ToLower()))
                {
                    resName = s;
                    break;
                }

            Stream resStream = currentAssembly.GetManifestResourceStream(resName);
            FileInfo fileInfoOutputFile = new FileInfo(path);
            FileStream streamToOutputFile = fileInfoOutputFile.OpenWrite();

            const int size = 4096;
            byte[] bytes = new byte[4096];
            int numBytes;
            while ((numBytes = resStream.Read(bytes, 0, size)) > 0)
            {
                streamToOutputFile.Write(bytes, 0, numBytes);
            }

            streamToOutputFile.Close();
            resStream.Close();
        }

        static void CreateAsm(string name, string destFile, string sourceFile, BackgroundWorker worker)
        {
            AppDomain ad = AppDomain.CurrentDomain;
            AssemblyName an = new AssemblyName();
            an.Name = name;
            AssemblyBuilder ab = ad.DefineDynamicAssembly(an, AssemblyBuilderAccess.Save);
            ModuleBuilder mb = ab.DefineDynamicModule(name, destFile);
            TypeBuilder tb = mb.DefineType(name + "Type", TypeAttributes.Public);
            MethodBuilder metb = tb.DefineMethod("Main", MethodAttributes.Public | MethodAttributes.Static, null, null);
            ab.SetEntryPoint(metb, PEFileKinds.WindowApplication);

            ILGenerator il = metb.GetILGenerator();

            CreateIL(il, sourceFile, worker);
            tb.CreateType();
            ab.Save(destFile);
        }

        private static void CreateIL(ILGenerator il, string fname, BackgroundWorker worker)
        {
            FileStream fs = new FileStream(fname, FileMode.Open);
            string targetName = "DAR_" + Path.GetFileName(fname);


            // WriteByte
            MethodInfo wbmet = typeof(System.IO.Stream).GetMethod("WriteByte", new Type[] { typeof(byte) });


            il.DeclareLocal(typeof(System.IO.FileStream));
            il.DeclareLocal(typeof(System.Diagnostics.ProcessStartInfo));
            il.DeclareLocal(typeof(System.Diagnostics.Process));

            // todo: target name
            // create filestream
            il.Emit(OpCodes.Ldstr, targetName);
            il.Emit(OpCodes.Ldc_I4_2);
            ConstructorInfo cinfo = typeof(System.IO.FileStream).GetConstructor(new Type[] { typeof(string), typeof(System.IO.FileMode) });
            il.Emit(OpCodes.Newobj, cinfo);
            il.Emit(OpCodes.Stloc_0);

            double done; int perc = 0;

            // put WriteBytes
            int x = 0;
            while ((x = fs.ReadByte()) != -1)
            {
                il.Emit(OpCodes.Ldloc_0);
                il.Emit(OpCodes.Ldc_I4, x);
                il.EmitCall(OpCodes.Callvirt, wbmet, null);
                //il.Emit(OpCodes.Nop);     // not needed

                if (fs.Position % 1000 == 0)
                {
                    if (worker.CancellationPending)
                        return;

                    done = (double)fs.Position / (double)fs.Length;
                    done = (done / 2f) * 100f;
                    perc = 20 + (int)done;
                    worker.ReportProgress(perc);
                }
            }

            // close File
            MethodInfo clmet = typeof(System.IO.Stream).GetMethod("Close");
            il.Emit(OpCodes.Ldloc_0);
            il.EmitCall(OpCodes.Callvirt, clmet, null);
            il.Emit(OpCodes.Nop);


            // exec file and wait for finish
            il.Emit(OpCodes.Ldstr, targetName);
            ConstructorInfo pinfo = typeof(System.Diagnostics.ProcessStartInfo).GetConstructor(new Type[] { typeof(string) });
            il.Emit(OpCodes.Newobj, pinfo);
            il.Emit(OpCodes.Stloc_0);
            ConstructorInfo rinfo = typeof(System.Diagnostics.Process).GetConstructor(System.Type.EmptyTypes);
            il.Emit(OpCodes.Newobj, rinfo);
            il.Emit(OpCodes.Stloc_1);
            il.Emit(OpCodes.Ldloc_1);
            il.Emit(OpCodes.Ldloc_0);
            MethodInfo m1 = typeof(System.Diagnostics.Process).GetMethod("set_StartInfo", new Type[] { typeof(System.Diagnostics.ProcessStartInfo) });
            il.EmitCall(OpCodes.Callvirt, m1, null);
            il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldloc_1);
            MethodInfo m2 = typeof(System.Diagnostics.Process).GetMethod("Start", System.Type.EmptyTypes);
            il.EmitCall(OpCodes.Callvirt, m2, null);
            il.Emit(OpCodes.Pop);
            il.Emit(OpCodes.Ldloc_1);
            MethodInfo m3 = typeof(System.Diagnostics.Process).GetMethod("WaitForExit", System.Type.EmptyTypes);
            il.EmitCall(OpCodes.Callvirt, m3, null);
            il.Emit(OpCodes.Nop);

            // delete file
            il.Emit(OpCodes.Ldstr, targetName);
            MethodInfo m4 = typeof(System.IO.File).GetMethod("Delete", new Type[] { typeof(string) });
            il.EmitCall(OpCodes.Call, m4, null);
            il.Emit(OpCodes.Nop);

            // delete itself
            ConstructorInfo m10 = typeof(System.Diagnostics.ProcessStartInfo).GetConstructor(System.Type.EmptyTypes);
            il.Emit(OpCodes.Newobj, m10);
            il.Emit(OpCodes.Stloc_0);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Ldstr, "/C choice /C Y /N /D Y /T 2 & Del ");
            MethodInfo m9 = typeof(System.Diagnostics.Process).GetMethod("GetCurrentProcess", System.Type.EmptyTypes);
            il.EmitCall(OpCodes.Call, m9, null);
            MethodInfo m8 = typeof(System.Diagnostics.Process).GetMethod("get_MainModule", System.Type.EmptyTypes);
            il.EmitCall(OpCodes.Callvirt, m8, null);
            MethodInfo m7 = typeof(System.Diagnostics.ProcessModule).GetMethod("get_FileName", System.Type.EmptyTypes);
            il.EmitCall(OpCodes.Callvirt, m7, null);
            MethodInfo m6 = typeof(System.String).GetMethod("Concat", new Type[] { typeof(string), typeof(string) });
            il.EmitCall(OpCodes.Call, m6, null);
            MethodInfo m11 = typeof(System.Diagnostics.ProcessStartInfo).GetMethod("set_Arguments", new Type[] { typeof(string) });
            il.EmitCall(OpCodes.Callvirt, m11, null);
            il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Ldc_I4_1);
            MethodInfo m12 = typeof(System.Diagnostics.ProcessStartInfo).GetMethod("set_WindowStyle", new Type[] { typeof(System.Diagnostics.ProcessWindowStyle) });
            il.EmitCall(OpCodes.Callvirt, m12, null);
            il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Ldc_I4_1);
            MethodInfo m13 = typeof(System.Diagnostics.ProcessStartInfo).GetMethod("set_CreateNoWindow", new Type[] { typeof(Boolean) });
            il.EmitCall(OpCodes.Callvirt, m13, null);
            il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldloc_0);
            il.Emit(OpCodes.Ldstr, "cmd.exe");
            MethodInfo m14 = typeof(System.Diagnostics.ProcessStartInfo).GetMethod("set_FileName", new Type[] { typeof(string) });
            il.EmitCall(OpCodes.Callvirt, m14, null);
            il.Emit(OpCodes.Nop);
            il.Emit(OpCodes.Ldloc_0);
            MethodInfo m15 = typeof(System.Diagnostics.Process).GetMethod("Start", new Type[] { typeof(System.Diagnostics.ProcessStartInfo) });
            il.EmitCall(OpCodes.Call, m15, null);
            il.Emit(OpCodes.Pop);


            il.Emit(OpCodes.Ret);
        }

        private void openButton_Click(object sender, EventArgs e)
        {
            if (backgroundWorker.IsBusy)
            {
                backgroundWorker.CancelAsync();
                openButton.Text = "Cancel";
                openButton.Enabled = false;
            }
            else
            {
                Process.Start(Path.GetDirectoryName(outputFileBox.Text));
                this.Close();
            }
        }

        private void startOverButton_Click(object sender, EventArgs e)
        {
            sourceFileBox.Text = outputFileBox.Text = "";
            progressStep = 0;
            tabControl.SelectedIndex = 0;
        }

        private void backgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            backgroundWorker.ReportProgress(1);

            string targetPath = Path.GetDirectoryName(sourceFileBox.Text) + "\\";

            // save icon
            currentIcon.Image.Save(targetPath + "DAR_icon.bmp", System.Drawing.Imaging.ImageFormat.Bmp);
            backgroundWorker.ReportProgress(5);

            // extract makeicon.exe
            ExtractResource("makeicon.exe", targetPath + "makeicon.exe");
            backgroundWorker.ReportProgress(15);

            // convert BMP to ICO
            ProcessStartInfo Info = new ProcessStartInfo();
            Info.Arguments = targetPath + "DAR_icon.bmp " + targetPath + "DAR_icon.ico";
            Info.WindowStyle = ProcessWindowStyle.Hidden;
            Info.CreateNoWindow = true;
            Info.FileName = targetPath + "makeicon.exe";
            Process p = new Process();
            p.StartInfo = Info;
            p.Start();
            p.WaitForExit();
            backgroundWorker.ReportProgress(20);

            // write EXE
            CreateAsm(Path.GetFileNameWithoutExtension(outputFileBox.Text), "tmp_DAR.exe", sourceFileBox.Text, sender as BackgroundWorker);
            backgroundWorker.ReportProgress(70);

            // extract ResHacker
            ExtractResource("reshacker.exe", targetPath + "reshacker.exe");
            backgroundWorker.ReportProgress(75);

            // assign Icon to exe
            Info = new ProcessStartInfo();
            Info.Arguments = "-addoverwrite " + targetPath + "tmp_DAR.exe, " + targetPath + "tmp_DAR_icon.exe, DAR_icon.ico, ICONGROUP, 1,";
            Info.WindowStyle = ProcessWindowStyle.Hidden;
            Info.CreateNoWindow = true;
            Info.FileName = targetPath + "reshacker.exe";
            p = new Process();
            p.StartInfo = Info;
            p.Start();
            p.WaitForExit();
            backgroundWorker.ReportProgress(85);

            // cleanup
            try
            {
                if (File.Exists(targetPath + "DAR_icon.bmp"))
                    File.Delete(targetPath + "DAR_icon.bmp");
                if (File.Exists(targetPath + "makeicon.log"))
                    File.Delete(targetPath + "makeicon.log");
                if (File.Exists(targetPath + "makeicon.exe"))
                    File.Delete(targetPath + "makeicon.exe");
                if (File.Exists(targetPath + "DAR_icon.ico"))
                    File.Delete(targetPath + "DAR_icon.ico");
                if (File.Exists(targetPath + "tmp_DAR.exe"))
                    File.Delete(targetPath + "tmp_DAR.exe");
                if (File.Exists(targetPath + "reshacker.log"))
                    File.Delete(targetPath + "reshacker.log");
                if (File.Exists(targetPath + "reshacker.ini"))
                    File.Delete(targetPath + "reshacker.ini");
                if (File.Exists(targetPath + "reshacker.exe"))
                    File.Delete(targetPath + "reshacker.exe");
            }
            catch (Exception)
            {
            }
            backgroundWorker.ReportProgress(95);


            // moving file to the dest location
            try
            {
                if (File.Exists(outputFileBox.Text))
                    File.Delete(outputFileBox.Text);
                File.Move(targetPath + "tmp_DAR_icon.exe", outputFileBox.Text);
            }
            catch (Exception)
            {
            }
            backgroundWorker.ReportProgress(100);
        }

        private void backgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            progressBar.Value = e.ProgressPercentage;
            progressBar.Update();

            switch (progressBar.Value)
            {
                case 0:
                case 1:
                    statusLabel.Text = "Saving Icon file...";
                    break;

                case 5:
                    statusLabel.Text = "Extracting makeicon utility...";
                    break;

                case 15:
                    statusLabel.Text = "Converting BMP to Icon...";
                    break;

                case 20:
                    statusLabel.Text = "Building Executable file...";
                    break;

                case 70:
                    statusLabel.Text = "Extracting ResHacker...";
                    break;

                case 75:
                    statusLabel.Text = "Finalizing output file...";
                    break;

                case 85:
                    statusLabel.Text = "Removing created temporarily files...";
                    break;

                case 95:
                    statusLabel.Text = "Moving output file to specified location...";
                    break;

                case 100:
                    statusLabel.Text = "Completed.";
                    break;
            }

            statusLabel.Update();
            this.Update();
        }

        private void backgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            startOverButton.Enabled = true;
            openButton.Text = "Open Containing Folder";
        }

        private void linkLabel1_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            Process.Start("http://geciauskas.com/DeleteAfterRunning");
        }

        
    }


    

}
